//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


//#include "stdafx.h"
#include "packets.h"
#include "otherfunctions.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

extern char* nstrdup(char const *mj)
{
  if(mj==NULL) {
    char* empty="";
    /* this really shouldn't happen */
    char* uus=new char[1];
    uus[0]=0;
    return uus;
  }

  char *uus=new char[strlen(mj)+1];
  memset(uus,0,(strlen(mj)+1));
  strcpy(uus,mj);
  return uus;
}

extern char* nstrdup(char *mj)
{
  if(mj==NULL) {
    char* empty="";
    /* this really shouldn't happen */
    char* uus=new char[1];
    uus[0]=0;
    return uus;
  }

  char *uus=new char[strlen(mj)+1];
  memset(uus,0,(strlen(mj)+1));
  strcpy(uus,mj);
  return uus;
}

Packet::Packet(uint8 protocol){
	m_bSplitted = false;
	m_bLastSplitted = false;
	size = 0;
	pBuffer = 0;
	completebuffer = 0;
	tempbuffer = 0;
	prot = protocol;
	m_bPacked = false;
}

Packet::Packet(char* header){
	m_bSplitted = false;
	m_bPacked = false;
	m_bLastSplitted = false;
	tempbuffer = 0;
	pBuffer = 0;
	completebuffer = 0;
	Header_Struct* head = (Header_Struct*) header;
	size = head->packetlength-1;
	opcode = head->command;
	prot = head->eDonkeyID;
}

Packet::Packet(char* pPacketPart, uint32 nSize ,bool bLast){// only used for splitted packets!
	m_bSplitted = true;
	m_bPacked = false;
	m_bLastSplitted = bLast;
	tempbuffer = 0;
	pBuffer = 0;
	completebuffer = pPacketPart;
	size = nSize-6;
}

Packet::Packet(int8 in_opcode,uint32 in_size,uint8 protocol){
	m_bSplitted = false;
	m_bPacked = false;
	m_bLastSplitted = false;
	tempbuffer = 0;
	if (in_size){
		completebuffer = new char[in_size+10];
		pBuffer = completebuffer+6;
		memset(completebuffer,0,in_size+10);
	}
	else{
		pBuffer = 0;
		completebuffer = 0;
	}
	opcode = in_opcode;
	size = in_size;
	prot = protocol;
}

Packet::Packet(CMemFile* datafile,uint8 protocol){
	m_bSplitted = false;
	m_bPacked = false;
	m_bLastSplitted = false;
	size = datafile->GetLength();
	completebuffer = new char[datafile->GetLength()+10];
	pBuffer = completebuffer+6;
	BYTE* tmp = datafile->Detach();;
	memcpy(pBuffer,tmp,size);
	free(tmp);
	tempbuffer = 0;
	prot = protocol;
}

Packet::~Packet(){
	if (completebuffer)
		delete[] completebuffer;
	else if (pBuffer)
		delete [] pBuffer;
	if (tempbuffer)
		delete[] tempbuffer;
}

char* Packet::GetPacket(){
	if (completebuffer){
		if (!m_bSplitted)
			memcpy(completebuffer,GetHeader(),6);
		return completebuffer;
	}
	else{
		if (tempbuffer)
			delete[] tempbuffer;
		tempbuffer = new char[size+10];
		memcpy(tempbuffer,GetHeader(),6);
		memcpy(tempbuffer+6,pBuffer,size);
		return tempbuffer;
	}
}

char* Packet::DetachPacket(){
	if (completebuffer){
		if (!m_bSplitted)
			memcpy(completebuffer,GetHeader(),6);
		char* result = completebuffer;
		completebuffer = 0;
		pBuffer = 0;
		return result;
	}
	else{
		if (tempbuffer)
			delete[] tempbuffer;
		tempbuffer = new char[size+10];
		memcpy(tempbuffer,GetHeader(),6);
		memcpy(tempbuffer+6,pBuffer,size);
		char* result = tempbuffer;
		tempbuffer = 0;
		return result;
	}
}

char* Packet::GetHeader(){
	assert ( !m_bSplitted );
	Header_Struct* header = (Header_Struct*) head;
	header->command = opcode;
	header->eDonkeyID =  prot;
	header->packetlength = size+1;
	return head;
}

char* Packet::GetUDPHeader(){
	assert ( !m_bSplitted );
	memset(head,0,6);
	UDP_Header_Struct* header = (UDP_Header_Struct*) head;
	header->command = opcode;
	header->eDonkeyID =  prot;
	return head;
}

void Packet::PackPacket(){
	assert (!m_bSplitted);
	BYTE* output = new BYTE[size+300];
	uLongf newsize = size+300;
	uint16 result = compress2(output,&newsize,(BYTE*)pBuffer,size,9);
	if (result != Z_OK || size <= newsize){
		delete[] output;
		return;
	}
	prot = OP_PACKEDPROT;
	memcpy(pBuffer,output,newsize);
	delete[] output;
	m_bPacked = true;
}

bool Packet::UnPackPacket(){
	assert ( prot == OP_PACKEDPROT); 
	uint32 nNewSize = size*10+300;
	if (nNewSize > 50000)
		nNewSize = 50000;
	BYTE* unpack = new BYTE[nNewSize];
	uLongf unpackedsize = nNewSize;
	uint16 result = uncompress(unpack,&unpackedsize,(BYTE*)pBuffer,size);
	if (result == Z_OK){
		assert ( completebuffer == NULL );
		assert ( pBuffer != NULL );
		size = unpackedsize;
		delete[] pBuffer;
		pBuffer = (char*)unpack;
		prot =  OP_EMULEPROT;
		return true;				
	}
	delete[] unpack;
	return false;
}


CTag::CTag(char* name,uint32 intvalue){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	tag->tagname = nstrdup(name);
	tag->type = 3;
	tag->intvalue = intvalue;
}

CTag::CTag(int8 special, uint32 intvalue){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	tag->type = 3;
	tag->intvalue = intvalue;
	tag->specialtag = special;
}

CTag::CTag(char* name,char* strvalue){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	tag->tagname = nstrdup(name);
	tag->type = 2;
	tag->stringvalue = nstrdup(strvalue);
}

CTag::CTag(int8 special, char* strvalue){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	tag->type = 2;
	tag->stringvalue = nstrdup(strvalue);
	tag->specialtag = special;
}

CTag::CTag(Tag_Struct* in_tag){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	memcpy(tag,in_tag,sizeof(Tag_Struct));
	if (in_tag->tagname) 
		tag->tagname = nstrdup(in_tag->tagname);
	if (in_tag->stringvalue)
		tag->stringvalue = nstrdup(in_tag->stringvalue);
}

CTag::CTag(CFile* in_data){
  tag = new Tag_Struct;
  memset(tag,0,sizeof(Tag_Struct));
  in_data->Read(&tag->type,1);
  uint16 length;
  in_data->Read(&length,2);
  if (length == 1)
    in_data->Read(&tag->specialtag,1);
  else {
    tag->tagname = new char[length+1];
    in_data->Read(tag->tagname,length);
    tag->tagname[length] = 0;
  }
  if (tag->type == 2){
    in_data->Read(&length,2);
    tag->stringvalue = new char[length+1];
    in_data->Read(tag->stringvalue,length);
    tag->stringvalue[length] = 0;
  }
  else if (tag->type == 3)
    in_data->Read(&tag->intvalue,4);
}

CTag::CTag(CMemFile* in_data){
  tag = new Tag_Struct;
  memset(tag,0,sizeof(Tag_Struct));
  in_data->Read(&tag->type,1);
  uint16 length;
  in_data->Read(&length,2);
  if (length == 1)
    in_data->Read(&tag->specialtag,1);
  else {
    tag->tagname = new char[length+1];
    in_data->Read(tag->tagname,length);
    tag->tagname[length] = 0;
  }
  if (tag->type == 2){
    in_data->Read(&length,2);
    tag->stringvalue = new char[length+1];
    in_data->Read(tag->stringvalue,length);
    tag->stringvalue[length] = 0;
  }
  else if (tag->type == 3)
    in_data->Read(&tag->intvalue,4);
}

CTag::CTag(FILE* in_data){
	tag = new Tag_Struct;
	memset(tag,0,sizeof(Tag_Struct));
	fread(&tag->type,1,1,in_data);
	uint16 length;
	fread(&length,2,1,in_data);
	if (length == 1)
		fread(&tag->specialtag,1,1,in_data);
	else {
		tag->tagname = new char[length+1];
		fread(tag->tagname,length,1,in_data);
		tag->tagname[length] = 0;
	}
	if (tag->type == 2){
		fread(&length,2,1,in_data);
		tag->stringvalue = new char[length+1];
		fread(tag->stringvalue,length,1,in_data);
		tag->stringvalue[length] = 0;
	}
	else if (tag->type == 3)
		fread(&tag->intvalue,4,1,in_data);
}

CTag::~CTag(){
	if (tag->tagname)
		delete[] tag->tagname;
	if (tag->stringvalue)
		delete[] tag->stringvalue;
	delete tag;
}

bool CTag::WriteTagToFile(CFile* file){
	file->Write(&tag->type,1);
	if (tag->tagname){
		uint16 taglen= (uint16)strlen(tag->tagname);
		file->Write(&taglen,2);
		file->Write(tag->tagname,taglen);
	}
	else{
		uint16 taglen = 1;
		file->Write(&taglen,2);
		file->Write(&tag->specialtag,taglen);
	}
	if (tag->type == 2){
		uint16 len = (uint16)strlen(tag->stringvalue);
		file->Write(&len,2);
		file->Write(tag->stringvalue,len);
	}
	else if (tag->type == 3)
		file->Write(&tag->intvalue,4);
	return true;
}

bool CTag::WriteTagToFile(CMemFile* file){
	file->Write(&tag->type,1);
	if (tag->tagname){
		uint16 taglen= (uint16)strlen(tag->tagname);
		file->Write(&taglen,2);
		file->Write(tag->tagname,taglen);
	}
	else{
		uint16 taglen = 1;
		file->Write(&taglen,2);
		file->Write(&tag->specialtag,taglen);
	}
	if (tag->type == 2){
		uint16 len = (uint16)strlen(tag->stringvalue);
		file->Write(&len,2);
		file->Write(tag->stringvalue,len);
	}
	else if (tag->type == 3)
		file->Write(&tag->intvalue,4);
	return true;
}

bool CTag::WriteTagToFile(FILE* file){
	fputc(tag->type,file);
	if (tag->tagname && (!tag->specialtag)){
		uint16 taglen= (uint16)strlen(tag->tagname);
		fwrite(&taglen,2,1,file);
		fwrite(tag->tagname,taglen,1,file);
	}
	else{
		uint16 taglen = 1;
		fwrite(&taglen,2,1,file);
		fwrite(&tag->specialtag,taglen,1,file);
	}
	if (tag->type == 2){
		uint16 len = (uint16)strlen(tag->stringvalue);
		fwrite(&len,2,1,file);
		fwrite(tag->stringvalue,len,1,file);
	}
	else if (tag->type == 3)
		fwrite(&tag->intvalue,4,1,file);
	return ferror(file);
}
